/*---------------------------------------
 * CURRENTLY UPDATING: 
 *       change over from Character to CharacterClass
 *       change CharacterClass to utilize the Stats class
*///-------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace CrystalWars
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        //States:
        //Waiting - waiting for user input
        //Moving - moving a unit to the selected tile
        //ShowMoves - Showing the tiles that unit can move to
        //UnitMenu - showing the battle menu for a selected unit: attack, move, wait, defend, item
        String testing, testing2;
        Stack<String> stateStack = new Stack<String>();
        Boolean[] current_unit_state = new Boolean[2]; //first slot is attack/skill/defend, second = move. 

        //Field States:
        //place characters, Waiting battle, character turn, attack, defend, move, skill (based on each skill, play different animation), 
        SpriteFont testFont;

        List<Unit> waitQ = new List<Unit>();
        List<Unit> readyQ = new List<Unit>();

        delegate bool del(Vector3 i);
        GraphicsDeviceManager graphics;
        int x = 0;
        int window_x = 1400, window_y = 800;
        SpriteBatch spriteBatch;
        //-----------------------
        KeyboardState oldState; //used for user input
        MouseState oldMouse;
        Vector2 mouse_displacement = Vector2.Zero, oldMouse_pos = Vector2.Zero;
        //-----------------------
        //Textures
        Texture2D field;
        Texture2D warrior;
        Texture2D medic;
        Texture2D harvester;
        Texture2D rogue;
        Texture2D marksman;
        Texture2D movementTile; //displays movement
        Texture2D skillTile; //displays action tiles, as in, the range of an attack so if an attack hits with a radius of 2, this tile highlights all those tiles.
        Texture2D selectedTile; //this is the tile that is currently selected.
        Texture2D instructions;
        //-----------------------
        Vector3 tile_offset = Vector3.Zero; // the pixel offset of the tiles. So at position 1,1 the unit image is offset by, say, 100 pixels both in X and Y.
        //Z position for tile_offset has yet to be used in our modules
        float hypotenuse; //hypotenuse of the tile_offset square
        float zoom_offset = 1.0f;
        List<Vector2> selected_tiles = new List<Vector2>();
        Unit unit_show_moves;

        Vector2 current_transition = Vector2.Zero; //moves between tiles. 
        Char current_direction; //moving rigt, left, up, down?

        String field_type = "Test_Field"; //Base3 or Test_Field
        Field field_object;
        List<Vector2> startTileslocation = new List<Vector2>();
        Char[,] movement_paths; //size 14x14 - basically stores the direction the parent tile would need to go to get to the child. 
                                //Origins all point to the current unit's position.
        Stack<Char> myPath = new Stack<Char>(); // this stores the path that the unit will have to take. Ex: Origin, R, R, D, L, U

        List<Vector2> movements; //list of movement directions. Ex: Vector2 (0, 1) - move up 1 in x direction on field.
        Character warrior_test;
        Character medic_test;
        Character harvester_test;
        Character rogue_test;
        Character marksman_test;
        CharacterClass cc_warrior, cc_medic, cc_harvester, cc_ghost, cc_marksman, cc_engineer;
        //Array units = new Unit[10];
        List<Unit> units = new List<Unit>();
        List<Unit> goodguys = new List<Unit>();
        List<Unit> badguys = new List<Unit>();
        Unit current_unit;
        Vector2 current_selectedTile = new Vector2(0, 0);
        //----------------------------------------------------
        //3D stuff
        // Set the 3D model to draw.
        Matrix projection;
        Matrix view;
        Vector2 camera_offset = new Vector2(100, 100);
        Vector2 character_tile = new Vector2(1, 1);
        float world_scale = 1;
        //---------------------
        Quad quad;
        Quad quadSprite;
        BasicEffect quadEffect;
        BasicEffect myEffect;
        Model myModel;
        Texture2D test;
        Vector2 testPosition = Vector2.Zero;
        Vector3 Position = Vector3.One;
        Vector3 CameraPosition = new Vector3(1400, 0, 0);
        float Zoom = 1000;
        float RotationY = 315.0f;
        float RotationX = 30.0f;
        float RotationZ = 0.0f;
        float current_rotation = 0; //used for rotating the screen
        DepthStencilState depthBufferState;
        //----------------------------------------------------

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            stateStack.Push("Waiting");
            stateStack.Push("PlaceCharacters");
            //3D
            graphics.PreferredBackBufferWidth = window_x;
            graphics.PreferredBackBufferHeight = window_y;
            //graphics.IsFullScreen = true;
            graphics.ApplyChanges();
            //---------------
            depthBufferState = new DepthStencilState();
            depthBufferState.DepthBufferEnable = true;
            IsMouseVisible = true;

            Position.X = -403;
            Position.Y = -176;
            Position.Z = 400;
            quadSprite = new Quad(Vector3.Zero, Vector3.Backward, Vector3.Up, 0.694f, 1); //for sprites
            quad = new Quad(Vector3.Zero, Vector3.Backward, Vector3.Up, 1, 1); //for tiles
            projection = Matrix.CreateOrthographic(window_x, window_y, -1000, 100000.0f);
            //Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45.0f),aspectRatio, 1.0f, 10000.0f);
            view = Matrix.CreateLookAt(new Vector3(0, 0, 1), Vector3.Zero, Vector3.Up);
            //Matrix.CreateLookAt(new Vector3(window_x / 2, 100, -window_y / 2), new Vector3(window_x / 2, 0, -window_y/2), Vector3.Forward);
            //--------------
            if(field_type.Equals("Test_Field")){
                field_object = new Field(new int[,] {
                { 7,7, 7, 8, 6,2,3,1,3,0,0,10,14,15},
                { 6,5, 5, 4, 4,3,1,2,4,6,6, 8,13,13},
                { 3,4, 2, 3, 1,3,1,0,3,5,7, 8,13,14},
                { 1,3, 1, 1, 0,1,0,0,1,2,2,10, 8, 7},
                { 0,0, 0,-1,-1,0,0,0,0,2,2, 2, 2, 1},
                { 0,0, 0,-1,-1,0,0,0,1,2,2, 2, 2, 0},
                {-1,0, 0,-1,-1,0,2,1,1,2,3, 3, 0, 0},
                {-1,0, 0, 0, 0,0,1,0,0,0,0, 0, 0, 0},
                { 0,0,-1, 0, 0,0,0,1,0,0,0, 0, 0, 0},
                { 0,0, 0, 0, 0,0,1,1,1,1,0, 0, 1, 1},
                { 0,0, 0, 0, 1,1,1,1,1,1,1, 1, 2, 2},
                { 0,0, 0, 1, 2,2,1,0,1,1,1, 1, 2, 3},
                { 0,1, 1, 2, 2,1,0,0,0,1,1, 0, 1, 3},
                { 1,1, 2, 2, 2,2,1,0,0,1,1, 1, 1, 3}
                });
                myModel = Content.Load<Model>("TestBoard");
                startTileslocation.Add(new Vector2(1.0f, 3.0f));
                startTileslocation.Add(new Vector2(2.0f, 3.0f));
                startTileslocation.Add(new Vector2(3.0f, 3.0f));
                startTileslocation.Add(new Vector2(4.0f, 3.0f));
                startTileslocation.Add(new Vector2(5.0f, 3.0f));
                startTileslocation.Add(new Vector2(1.0f, 2.0f));
                startTileslocation.Add(new Vector2(2.0f, 2.0f));
                startTileslocation.Add(new Vector2(3.0f, 2.0f));
                startTileslocation.Add(new Vector2(4.0f, 2.0f));
                startTileslocation.Add(new Vector2(5.0f, 2.0f));
            }else{
                field_object = new Field(new int[,] {
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,10,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,1,1,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,2,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0}
                });
                myModel = Content.Load<Model>("Base3");
                startTileslocation.Add(new Vector2(1.0f, 3.0f));
                startTileslocation.Add(new Vector2(2.0f, 3.0f));
                startTileslocation.Add(new Vector2(3.0f, 3.0f));
                startTileslocation.Add(new Vector2(4.0f, 3.0f));
                startTileslocation.Add(new Vector2(5.0f, 3.0f));
                startTileslocation.Add(new Vector2(6.0f, 2.0f)); 
            }

            test = Content.Load<Texture2D>("testy_mctest");          
            quadEffect = new BasicEffect(graphics.GraphicsDevice);
            //--------------------------------------------------------------------------------------------------------------------------
            instructions = Content.Load<Texture2D>("instructions");
            // TODO: Add your initialization logic here
            oldState = Keyboard.GetState();
            oldMouse = Mouse.GetState();
            //--------------------------------------------------------------------------------------------------------------------------
            tile_offset.X = 100;
            tile_offset.Y = 100;
            hypotenuse = (float)Math.Sqrt((Math.Pow((double)tile_offset.X, 2.0) + Math.Pow((double)tile_offset.Y, 2.0)));
            //field = Content.Load<Texture2D>("Test_Field");
            movementTile = Content.Load<Texture2D>("movementTile");
            skillTile = Content.Load<Texture2D>("skillTile");
            selectedTile = Content.Load<Texture2D>("selectedTile");
            //--------------------------------------------------------------------------------------------------------------------------
            warrior = Content.Load<Texture2D>("warrior");
            warrior_test = new Character(warrior, test, warrior, "Test warrior", 1, 1, 1, 1, 1, 1, 10, 1, 3, 2, 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A');
            medic = Content.Load<Texture2D>("medic");
            medic_test = new Character(medic, test, medic, "Test medic", 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A');
            harvester = Content.Load<Texture2D>("mage");
            harvester_test = new Character(harvester, test, harvester, "Test harvester", 1, 1, 1, 1, 1, 1, 1, 1, 2,3, 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A');
            rogue = Content.Load<Texture2D>("rogue");
            rogue_test = new Character(rogue, test, rogue, "Test rogue", 1, 1, 1, 1, 1, 1, 1, 1, 4,3, 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A');
            marksman = Content.Load<Texture2D>("marksman");
            marksman_test = new Character(marksman, test, marksman, "Test marksman", 1, 1, 1, 1, 1, 1, 5, 1, 6,3, 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A');
            //---------
            cc_harvester = new CharacterClass("harvester", harvester, test, harvester);
            cc_marksman = new CharacterClass("marksman", marksman, test, marksman,);
            //cc_engineer = new CharacterClass("engineer");
            cc_warrior = new CharacterClass("warrior", warrior, test, warrior);
            cc_ghost = new CharacterClass("ghost", rogue, test, rogue);
            cc_medic = new CharacterClass("medic", medic, test, medic);
            //--------------------------------------------------------------------------------------------------------------------------
            goodguys.Add(new Unit(cc_warrior));
            goodguys.Add(new Unit(cc_medic));
            goodguys.Add(new Unit(cc_harvester));
            goodguys.Add(new Unit(cc_ghost)); //rogue, shadow dancer, charlatan, ghost
            goodguys.Add(new Unit(cc_marksman));
            //goodguys[0].set_position(new Vector2(1.0f, 3.0f));
            //goodguys[1].set_position(new Vector2(2.0f, 3.0f));
            //goodguys[2].set_position(new Vector2(3.0f, 3.0f));
            //goodguys[3].set_position(new Vector2(4.0f, 3.0f));
            //goodguys[4].set_position(new Vector2(5.0f, 3.0f));
            //--------------------------------------------------------------------------------------------------------------------------


            
            current_unit = goodguys[0];
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent(){
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            field = Content.Load<Texture2D>("Test_Field");
            testFont = Content.Load<SpriteFont>("Arial");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            //Look for key strokes
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();
            UpdateInput();
            //------------------
            // TODO: Add your update logic here
            if (stateStack.Peek().Equals("Moving"))
            {
                camera_offset = current_unit.get_position() * 100 + current_transition;
                if (myPath.Peek() == 'R' && Math.Abs(current_transition.X) < tile_offset.X){
                    current_transition.X += 5;
                }else if (myPath.Peek() == 'L' && Math.Abs(current_transition.X) < tile_offset.X){
                    current_transition.X -= 5;
                }else if (myPath.Peek() =='U' && Math.Abs(current_transition.Y) < tile_offset.Y){
                    current_transition.Y -= 5;
                }else if (myPath.Peek() == 'D' && Math.Abs(current_transition.Y) < tile_offset.Y){
                    current_transition.Y += 5;
                }else{
                    current_transition = Vector2.Zero;
                    if (myPath.Peek().Equals('R')){
                        current_unit.set_x_position((int)current_unit.get_position().X + 1);
                    }else if (myPath.Peek().Equals('L')){
                        current_unit.set_x_position((int)current_unit.get_position().X - 1);
                    }else if (myPath.Peek().Equals('U')){
                        current_unit.set_y_position((int)current_unit.get_position().Y - 1);
                    }else if (myPath.Peek().Equals('D')){
                        current_unit.set_y_position((int)current_unit.get_position().Y + 1);
                    }
                    SortUnits();
                    myPath.Pop();
                    if (myPath.Count == 0) { stateStack.Pop(); stateStack.Pop(); }
                }
            }else if(stateStack.Peek().Equals("Rotating")){
                if (current_direction.Equals('R') && Math.Abs(current_rotation) < 90){
                    current_rotation -= 5;
                }else if (current_direction.Equals('L') && Math.Abs(current_rotation) < 90){
                    current_rotation += 5;
                }else{
                    stateStack.Pop();
                    if (current_direction.Equals('R')){
                        RotationY -= 90;
                        current_rotation = 0;
                    }else if(current_direction.Equals('L')){
                        RotationY += 90;
                        current_rotation = 0;
                    }
                    if (RotationY >= 360) { RotationY = 45; }
                    if (RotationY < 0) { RotationY = 315; }
                    SortUnits();
                }
            }

            //state = gameTime.TotalGameTime.Milliseconds.ToString();
            
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        //---------------------------------------------------------------------------------------------------------
        //---------------------------------------------------------------------------------------------------------
        //3D stuff
        private void DrawModel(Model m)
        {
            Matrix[] transforms = new Matrix[m.Bones.Count];
            float aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;
            m.CopyAbsoluteBoneTransformsTo(transforms);
            view = Matrix.CreateLookAt(new Vector3(0, 0, 1000), Vector3.Zero, Vector3.Up);

            foreach (ModelMesh mesh in m.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    //effect.EnableDefaultLighting();
                    effect.View = view;
                    effect.Projection = projection;
                    effect.World =
                            transforms[mesh.ParentBone.Index]
                        //* Matrix.CreateScale(1.182f) //where the hypotenuse = 100 pixels
                            * Matrix.CreateScale(1.666f) //where each side of a square is 100 pixels
                        //* Matrix.CreateTranslation(new Vector3(0, -219, 0)) //used when the board origin is placed on the bottom of the model, instead of at tile height 0
                            * Matrix.CreateRotationX(MathHelper.ToRadians(90))
                            * Matrix.CreateTranslation(new Vector3(-50, -1400 + 50, 0))
                            * Matrix.CreateTranslation(new Vector3(-camera_offset.X, camera_offset.Y, 0))
                            * Matrix.CreateRotationX(MathHelper.ToRadians(-90))
                            * Matrix.CreateRotationY(MathHelper.ToRadians(RotationY + current_rotation))
                            * Matrix.CreateRotationX(MathHelper.ToRadians(RotationX))
                            * Matrix.CreateScale(zoom_offset)
                            * Matrix.CreateTranslation(new Vector3(-mouse_displacement.X, mouse_displacement.Y, 0))
                        //*/
                            ;
                }
                mesh.Draw();
            }

        }
        public void DrawSprites(List<Unit> unitzorz)
        {   // Create a new SpriteBatch, which can be used to draw textures.
            Vector2 moving = new Vector2(0,0);
            quadEffect = new BasicEffect(graphics.GraphicsDevice);
            view = Matrix.CreateLookAt(new Vector3(0, 0, 150), Vector3.Zero, Vector3.Up);
            quadEffect.View = view;
            quadEffect.Projection = projection;
            quadEffect.TextureEnabled = true;
            foreach (Unit unit in unitzorz)
            {
                if (unit.Equals(current_unit)){
                    moving = current_transition;
                }
                quadEffect.World = Matrix.Identity
                    //* Matrix.CreateScale(2)
                    * Matrix.CreateScale(203)
                    * Matrix.CreateTranslation(new Vector3(0, 100, 0))
                    * Matrix.CreateRotationY(MathHelper.ToRadians(-RotationY - current_rotation))
                    * Matrix.CreateTranslation(new Vector3(unit.get_position().X * 100 - camera_offset.X + moving.X, field_object.get_tile_height(unit.get_position()) * 20, unit.get_position().Y * 100 - camera_offset.Y + moving.Y))
                    * Matrix.CreateRotationY(MathHelper.ToRadians(RotationY + current_rotation))
                    * Matrix.CreateRotationX(MathHelper.ToRadians(RotationX))
                    * Matrix.CreateScale(zoom_offset)
                    * Matrix.CreateTranslation(0, 0, -850) //places the sprite directy on top of the field.
                    * Matrix.CreateTranslation(new Vector3(-mouse_displacement.X, mouse_displacement.Y, 0))//displaces camera according to the mouse input
                    //*/
                    ;
                moving = new Vector2(0,0);
                quadEffect.Texture = unit.get_frontside_avatar();

                foreach (EffectPass pass in quadEffect.CurrentTechnique.Passes)
                {
                    pass.Apply();
                    GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, quadSprite.Vertices, 0, 4, quadSprite.Indexes, 0, 2);
                }
            }
        }
        public void DrawTiles(List<Vector2> tiles, Texture2D tile_tex)
        {   // Create a new SpriteBatch, which can be used to draw textures.
            quadEffect = new BasicEffect(graphics.GraphicsDevice);
            view = Matrix.CreateLookAt(new Vector3(0, 0, 150), Vector3.Zero, Vector3.Up);
            quadEffect.View = view;
            quadEffect.Projection = projection;
            quadEffect.TextureEnabled = true;
            quadEffect.Texture = tile_tex;
            foreach (Vector2 tile in tiles)
            {
                    quadEffect.World = Matrix.Identity
                        //* Matrix.CreateScale(2)
                        * Matrix.CreateScale(101)
                        * Matrix.CreateTranslation(new Vector3(0, 0, 0))
                        * Matrix.CreateRotationX(MathHelper.ToRadians(-90))
                        * Matrix.CreateTranslation(new Vector3(tile.X * 100 - camera_offset.X, field_object.get_tile_height(tile) * 20, tile.Y * 100 - camera_offset.Y))
                        * Matrix.CreateRotationY(MathHelper.ToRadians(RotationY + current_rotation))
                        * Matrix.CreateRotationX(MathHelper.ToRadians(RotationX))
                        * Matrix.CreateScale(zoom_offset)
                        * Matrix.CreateTranslation(0, 0, -848)//0) //places the sprite directy on top of the field.
                        * Matrix.CreateTranslation(new Vector3(-mouse_displacement.X, mouse_displacement.Y, 0))//displaces camera according to the mouse input
                        //*/
                        ;
                    foreach (EffectPass pass in quadEffect.CurrentTechnique.Passes)
                    {
                        pass.Apply();
                        GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionNormalTexture>(PrimitiveType.TriangleList, quad.Vertices, 0, 4, quad.Indexes, 0, 2);
                    }
            }

        }
        //---------------------------------------------------------------------------------------------------------
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            GraphicsDevice.DepthStencilState = depthBufferState;
            //GraphicsDevice.BlendState = BlendState.Additive; //color burn effect, probably will use for special effects later.

            graphics.IsFullScreen = true;

            // TODO: Add your drawing code here
            //spriteBatch.Waiting(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
            spriteBatch.Begin();
            //-----------------------------------------------------
            //drawing a selected tile
            DrawModel(myModel);
            if(stateStack.Peek().Equals("ShowMoves")){DrawTiles(unit_show_moves.selected_tiles, movementTile);}
            if(stateStack.Peek().Equals("ShowSkillMoves")){DrawTiles(current_unit.selected_tiles, skillTile);} //change the List to list of tiles where the skill can be used.
            if (stateStack.Peek().Equals("PlaceCharacters")) { DrawTiles(startTileslocation, skillTile); } //initialize where the units are.
            DrawTiles(new List<Vector2>(){current_selectedTile}, selectedTile); //draw the selected tile where the cursor is.
            
            DrawSprites(units);
            //drawing some font
            spriteBatch.DrawString(testFont, "Current State: " + stateStack.Peek(), new Vector2(150.0f, 10.0f), Color.Black);
            spriteBatch.DrawString(testFont, "Current Unit: " + current_unit.get_character().get_type(), new Vector2(150.0f, 30.0f), Color.Black);
            spriteBatch.DrawString(testFont, "ReadyQ Units: " + readyQ.Count, new Vector2(150.0f, 50.0f), Color.Black);
            spriteBatch.DrawString(testFont, "Mouse Scroll Wheel: " + oldMouse.ScrollWheelValue, new Vector2(150.0f, 70.0f), Color.Black);
            spriteBatch.DrawString(testFont, "Current Rotation: " + RotationY, new Vector2(150.0f, 90.0f), Color.Black);
            spriteBatch.Draw(instructions, new Vector2(window_x - instructions.Width, 0), Color.White);

            spriteBatch.End();

            base.Draw(gameTime);
        }
        //----------------------------------------------------------------------------------------------------------------------
        //----------------------------------------------------------------------------------------------------------------------
        //----------------------------------------------------------------------------------------------------------------------
        //Our functions
        public void FindMovementTiles(Unit unit) //recursive program that finds the available tiles that a unit can move to
        {
            unit.selected_tiles = new List<Vector2>();
            unit.selected_tiles.Add(unit.get_position());
            int movement = unit.get_stats().get_movement();//.Stats.get_movement();
            Vector2 blah;
            List<Vector2> temp = new List<Vector2>();
            List<Vector3> temp3 = new List<Vector3>();
            if(unit == current_unit){ //remove out of bounds junk later
                //movement_paths.Initialize();
                blah = new Vector2(unit.get_position().X, unit.get_position().Y - 1);
                if(unit.get_position().Y > 0 && field_object.get_tile_height(blah) != -1 && field_object.get_tile_height(blah) - field_object.get_tile_height(unit.get_position()) <= unit.Stats.get_jump()){
                    temp3.Add(new Vector3(unit.get_position().X, unit.get_position().Y - 1.0f, movement-1)); //field position
                    unit.selected_tiles.Add(new Vector2(unit.get_position().X, unit.get_position().Y - 1.0f));
                    movement_paths[movement, movement - 1] = 'U';
                } blah = new Vector2(unit.get_position().X, unit.get_position().Y + 1);
                if (unit.get_position().Y < field_object.get_height() && field_object.get_tile_height(blah) != -1 && field_object.get_tile_height(blah) - field_object.get_tile_height(unit.get_position()) <= unit.Stats.get_jump())
                {
                    temp3.Add(new Vector3(unit.get_position().X, unit.get_position().Y + 1.0f, movement-1));
                    unit.selected_tiles.Add(new Vector2(unit.get_position().X, unit.get_position().Y + 1.0f));
                    movement_paths[movement, movement + 1] = 'D';
                } blah = new Vector2(unit.get_position().X + 1, unit.get_position().Y);
                if (unit.get_position().X < field_object.get_width() && field_object.get_tile_height(blah) != -1 && field_object.get_tile_height(blah) - field_object.get_tile_height(unit.get_position()) <= unit.Stats.get_jump())
                {
                    temp3.Add(new Vector3(unit.get_position().X + 1.0f, unit.get_position().Y, movement-1));
                    unit.selected_tiles.Add(new Vector2(unit.get_position().X + 1, unit.get_position().Y));
                    movement_paths[movement + 1, movement] = 'R';
                } blah = new Vector2(unit.get_position().X - 1, unit.get_position().Y);
                if (unit.get_position().X > 0 && field_object.get_tile_height(blah) != -1 && field_object.get_tile_height(blah) - field_object.get_tile_height(unit.get_position()) <= unit.Stats.get_jump())
                {
                    temp3.Add(new Vector3(unit.get_position().X - 1.0f, unit.get_position().Y, movement-1));
                    unit.selected_tiles.Add(new Vector2(unit.get_position().X - 1, unit.get_position().Y));
                    movement_paths[movement - 1, movement] = 'L';
                }if (movement > 1 && temp3.Count > 0){   
                    FindCurrentMovementTiles(unit,new List<Vector3>(),temp3);
                }
                /*temp.Add(new Vector2(1, 1));
                temp.Add(new Vector2(0, 0));
                unit.selected_tiles.AddRange(temp);
                 */
                unit.selected_tiles.Remove(new Vector2(unit.get_position().X, unit.get_position().Y));
            }else{
                if(unit.get_position().Y > 0){
                    temp.Add(new Vector2(unit.get_position().X, unit.get_position().Y - 1.0f));
                }if (unit.get_position().Y < field_object.get_height()){
                    temp.Add(new Vector2(unit.get_position().X, unit.get_position().Y + 1.0f));
                }if(unit.get_position().X > 0){
                    temp.Add(new Vector2(unit.get_position().X - 1.0f, unit.get_position().Y));
                }if(unit.get_position().X < field_object.get_width()){
                    temp.Add(new Vector2(unit.get_position().X + 1.0f, unit.get_position().Y));
                }
                if (movement > 0){
                    FindMovementTiles(unit, temp, movement);
                }
                unit.selected_tiles.Remove(new Vector2(unit.get_position().X, unit.get_position().Y));
            }
        }
        public void FindMovementTiles(Unit unit, List<Vector2> moving_tiles, int movement_left)
        {
            Vector2 temp_tile;
            List<Vector2> new_moving_tiles = new List<Vector2>();
            unit.selected_tiles.AddRange(moving_tiles);
            foreach (Vector2 tile in moving_tiles)
            {
                temp_tile = new Vector2(tile.X, tile.Y - 1.0f);
                if (tile.Y > 0 && !unit.selected_tiles.Contains(temp_tile) && !new_moving_tiles.Contains(temp_tile)){
                    new_moving_tiles.Add(temp_tile);
                }
                temp_tile = new Vector2(tile.X, tile.Y + 1.0f);
                if (tile.Y < field_object.get_width() && !unit.selected_tiles.Contains(temp_tile) && !new_moving_tiles.Contains(temp_tile)){
                    new_moving_tiles.Add(temp_tile);
                }
                temp_tile = new Vector2(tile.X - 1.0f, tile.Y);
                if (tile.X > 0 && !unit.selected_tiles.Contains(temp_tile) && !new_moving_tiles.Contains(temp_tile)){
                    new_moving_tiles.Add(temp_tile);
                }
                temp_tile = new Vector2(tile.X + 1.0f, tile.Y);
                if (tile.X < field_object.get_height() && !unit.selected_tiles.Contains(temp_tile) && !new_moving_tiles.Contains(temp_tile)){
                    new_moving_tiles.Add(temp_tile);
                }
            }
            if (movement_left - 1 > 0)
            {
                FindMovementTiles(unit, new_moving_tiles, movement_left - 1);
            }
        }
        public void FindCurrentMovementTiles(Unit unit, List<Vector3> waiting_tiles, List<Vector3> moving_tiles) //used to keep track of movement paths of the current unit
        {   //moving tiles move in straight lines until blocked then become waiting
            //waiting tiles are waiting to be moved until moving tiles have all hit road blocks
            //once waiting, tiles split and try to go outwards in a straight line.
            Vector2 temp_tile2, temp_tile2_2, BLAH;
            Vector3 temp_tile3;
            List<Vector3> new_moving_tiles = new List<Vector3>();
            Char now_moving;
            del myDelegate, myDelegate2;
            if (moving_tiles.Count != 0){   
                foreach (Vector3 tile in moving_tiles){
                    //unit.selected_tiles.Add(new Vector2(tile.X, tile.Y));
                    if (tile.Z > 0){   //path format
                        BLAH = new Vector2(unit.get_stats().get_movement() + tile.X - unit.get_position().X, unit.get_stats().get_movement() + tile.Y - unit.get_position().Y);
                        now_moving = movement_paths[(int)BLAH.X, (int)BLAH.Y];
                        if (now_moving == 'R'){
                            temp_tile2 = new Vector2(1.0f, 0);
                            myDelegate = t => t.X < field_object.get_width();
                        }else if(now_moving == 'L'){
                            temp_tile2 = new Vector2(-1.0f, 0);
                            myDelegate = t => t.X > 0;
                        }else if(now_moving == 'U'){
                            temp_tile2 = new Vector2(0,-1.0f);
                            myDelegate = t => t.Y > 0;
                        }else{//now_moving == D
                            temp_tile2 = new Vector2(0, 1.0f);
                            myDelegate = t => t.Y < field_object.get_height();
                        }
                        temp_tile3 = new Vector3(temp_tile2.X+tile.X, temp_tile2.Y+tile.Y, tile.Z - 1);//field coordinates
                        if (!unit.selected_tiles.Contains(new Vector2(temp_tile3.X, temp_tile3.Y)) && myDelegate(tile) && field_object.get_tile_height(new Vector2(temp_tile3.X, temp_tile3.Y)) != -1)
                        {//&& !Contains_From(new_moving_tiles, temp_tile3)){
                            new_moving_tiles.Add(temp_tile3);
                            unit.selected_tiles.Add(new Vector2(temp_tile3.X, temp_tile3.Y));
                            movement_paths[(int)(BLAH.X + temp_tile2.X), (int)(BLAH.Y + temp_tile2.Y)] = now_moving; //path coordinates
                        }
                        waiting_tiles.Add(tile);
                    }  
                }

            }else{
                foreach(Vector3 tile in waiting_tiles){
                    //unit.selected_tiles.Add(new Vector2(tile.X, tile.Y));
                    if (tile.Z > 0){
                        //used for path coordinates
                        BLAH = new Vector2(unit.get_stats().get_movement() + tile.X - unit.get_position().X, unit.get_stats().get_movement() + tile.Y - unit.get_position().Y);
                        now_moving = movement_paths[(int)BLAH.X, (int)BLAH.Y]; 
                        if(now_moving == 'L' || now_moving == 'R'){
                            temp_tile2 = new Vector2(0, 1.0f);
                            myDelegate = t => t.Y < field_object.get_height();
                            now_moving = 'D';
                            //---------
                            temp_tile2_2 = new Vector2(0, -1.0f);
                            myDelegate2 = t => t.Y > 0;
                        }else{
                            temp_tile2 = new Vector2(- 1.0f,0);
                            myDelegate = t => t.X > 0;
                            now_moving = 'L';
                            //---------
                            temp_tile2_2 = new Vector2(1.0f, 0);
                            myDelegate2 = t => t.X < field_object.get_width();
                        }
                        temp_tile3 = new Vector3(temp_tile2.X + tile.X, temp_tile2.Y + tile.Y, tile.Z - 1);//field coordinates
                        //&& !Contains_From(new_moving_tiles, temp_tile3)){
                        if (!unit.selected_tiles.Contains(new Vector2(temp_tile3.X, temp_tile3.Y)) && myDelegate(tile)&& field_object.get_tile_height(new Vector2(temp_tile3.X, temp_tile3.Y)) != -1 ){//&& !Contains_From(new_moving_tiles, temp_tile3)){
                            new_moving_tiles.Add(temp_tile3);
                            unit.selected_tiles.Add(new Vector2(temp_tile3.X, temp_tile3.Y));
                            movement_paths[(int)(BLAH.X + temp_tile2.X), (int)(BLAH.Y + temp_tile2.Y)] = now_moving; //path coordinates
                        }
                        temp_tile3 = new Vector3(temp_tile2_2.X + tile.X, temp_tile2_2.Y + tile.Y, tile.Z - 1);//field coordinates
                        if (!unit.selected_tiles.Contains(new Vector2(temp_tile3.X, temp_tile3.Y)) && myDelegate2(tile)&& field_object.get_tile_height(new Vector2(temp_tile3.X, temp_tile3.Y)) != -1 ){//&& !Contains_From(new_moving_tiles, temp_tile3)){
                            new_moving_tiles.Add(temp_tile3);
                            unit.selected_tiles.Add(new Vector2(temp_tile3.X, temp_tile3.Y));
                            movement_paths[(int)(BLAH.X + temp_tile2_2.X), (int)(BLAH.Y + temp_tile2_2.Y)] = (now_moving=='D'?'U':'R'); //path coordinates
                        }
                    }
                }
                waiting_tiles = new List<Vector3>();
            }
            if(!(waiting_tiles.Count == 0 && new_moving_tiles.Count == 0)){
                FindCurrentMovementTiles(unit, waiting_tiles, new_moving_tiles);
            }
        }
        public Boolean Contains_From(List<Vector3> list3, Vector3 v3){
            Vector2 temp1, temp2;
            Boolean b = false;
            foreach(Vector3 vector in list3){
                temp1 = new Vector2(vector.X, vector.Y);
                temp2 = new Vector2(v3.X, v3.Y);
                if (temp1.Equals(temp2)) { b = true; }
            }
            return b;
        }
        private void UpdateInput()
        {
            zoom_offset = 1 + (oldMouse.ScrollWheelValue/120 * 0.09f);
            int tempX = (int)current_unit.get_position().X;
            int tempY = (int)current_unit.get_position().Y;
            KeyboardState newState = Keyboard.GetState();
            MouseState newMouse = Mouse.GetState();
            if (stateStack.Contains("Waiting"))
            {
                if(newMouse.LeftButton == ButtonState.Pressed && oldMouse.LeftButton != ButtonState.Pressed){
                    oldMouse_pos = new Vector2(newMouse.X, newMouse.Y) + mouse_displacement;
                }if(newMouse.LeftButton == ButtonState.Pressed && oldMouse.LeftButton == ButtonState.Pressed){
                    mouse_displacement = oldMouse_pos - new Vector2(newMouse.X, newMouse.Y);
                }if (oldState.IsKeyDown(Keys.Right) && !newState.IsKeyDown(Keys.Right) && FindMovement('R') ){
                        MoveSelectedTile();
                }if (oldState.IsKeyDown(Keys.Left) && !newState.IsKeyDown(Keys.Left) && FindMovement('L')){
                        MoveSelectedTile();
                }if (oldState.IsKeyDown(Keys.Up) && !newState.IsKeyDown(Keys.Up) && FindMovement('U')){
                        MoveSelectedTile();
                }if (oldState.IsKeyDown(Keys.Down) && !newState.IsKeyDown(Keys.Down) && FindMovement('D')){
                        MoveSelectedTile();
                }if (oldState.IsKeyDown(Keys.A) && !newState.IsKeyDown(Keys.A)){
                        stateStack.Push("Rotating");
                        current_direction = 'L';
                }if (oldState.IsKeyDown(Keys.D) && !newState.IsKeyDown(Keys.D)){
                        stateStack.Push("Rotating");
                        current_direction = 'R';
                }if (newState.IsKeyDown(Keys.Escape)){
                        Exit();
                }
            }if (stateStack.Peek().Equals("Waiting")){
                /*if (oldState.IsKeyDown(Keys.Right) && !newState.IsKeyDown(Keys.Right) && FindMovement('R') ){
                    state = "Moving";
                    SortUnits();
                }if (oldState.IsKeyDown(Keys.Left) && !newState.IsKeyDown(Keys.Left) && FindMovement('L')){
                    state = "Moving";
                    SortUnits();
                }if (oldState.IsKeyDown(Keys.Up) && !newState.IsKeyDown(Keys.Up) && FindMovement('U')){
                    state = "Moving";
                    SortUnits();
                }if (oldState.IsKeyDown(Keys.Down) && !newState.IsKeyDown(Keys.Down) && FindMovement('D')){
                    state = "Moving";
                    SortUnits();*/
                if (oldState.IsKeyDown(Keys.Space) && !newState.IsKeyDown(Keys.Space)){
                    //if (current_unit.selected_tiles.Count == 0) { FindMovementTiles(current_unit, true); }
                    camera_offset = current_unit.get_position() * 100;
                    unit_show_moves = current_unit;
                    stateStack.Push("ShowMoves");
                }if (oldState.IsKeyDown(Keys.Enter) && !newState.IsKeyDown(Keys.Enter)){ //Character turn switcher, aka end of a unit's turn
                    foreach(Unit unit in units){
                        if(current_selectedTile == unit.get_position()){
                            camera_offset = unit.get_position() * 100;
                            if (unit.selected_tiles.Count == 0) { FindMovementTiles(unit); }
                            unit_show_moves = unit;
                            stateStack.Push("ShowMoves");
                        }
                    }
                }if(oldState.IsKeyDown(Keys.W) && !newState.IsKeyDown(Keys.W)){
                    int position = units.IndexOf(current_unit) + 1;
                    if (position >= units.Count){
                        position = 0;
                    }
                    UpdateQ();
                }
            }else if (stateStack.Peek().Equals("PlaceCharacters")){
                if (oldState.IsKeyDown(Keys.Enter) && !newState.IsKeyDown(Keys.Enter) && startTileslocation.Contains(current_selectedTile)){
                    goodguys[x].set_position(current_selectedTile);
                    units.Add(goodguys[x]);
                    startTileslocation.Remove(current_selectedTile);
                    x++;
                    if(x==goodguys.Count){
                        InitializeQ();//initializes ready queue
                        UpdateQ();
                        //current_unit = units[0];
                        stateStack.Pop();
                        x++;
                    }
                    SortUnits();
                }
                if (x == goodguys.Count) { stateStack.Pop(); }
            }else if (stateStack.Peek().Equals("ShowMoves")){
                if (oldState.IsKeyDown(Keys.Space) && !newState.IsKeyDown(Keys.Space)){
                    stateStack.Pop();
                }if (oldState.IsKeyDown(Keys.Enter) && !newState.IsKeyDown(Keys.Enter) && current_unit.selected_tiles.Contains(current_selectedTile) && unit_show_moves == current_unit){
                    bool b = true;
                    foreach (Unit unit in units) { if (unit.get_position().Equals(current_selectedTile)) { b = false; } }
                    if(b){
                        //Find the movement path to the origin.
                        //FindMovementPath(currentUnit.get_position(), current_selectedTile);
                        Vector2 temp = new Vector2(current_selectedTile.X - current_unit.get_position().X + current_unit.get_stats().get_movement(),
                                                   current_selectedTile.Y - current_unit.get_position().Y + current_unit.get_stats().get_movement());
                        Vector2 target = new Vector2(current_unit.get_stats().get_movement(), current_unit.get_stats().get_movement());
                        testing = "";
                        do{
                            myPath.Push(movement_paths[(int)temp.X, (int)temp.Y]);
                            temp = convertDirection(temp);
                            testing += myPath.Peek() + ",";
                        } while(!(temp.X == target.X && temp.Y == target.Y));
                        mouse_displacement = Vector2.Zero;
                        stateStack.Push("Moving");
                    }
                }
            }
            // Update saved state.
            oldState = newState;
            oldMouse = newMouse;
        }
        public Vector2 convertDirection(Vector2 current_position){
            Vector2 temp = current_position;
            if (myPath.Peek() == 'R'){
                temp.X -= 1;
            }if (myPath.Peek() == 'L'){
                temp.X += 1;
            }if (myPath.Peek() == 'U'){
                temp.Y += 1;
            }if (myPath.Peek() == 'D'){
                temp.Y -= 1;
            }
            return temp;
        }
        public void InitializeQ()
        {
            foreach (Unit u in units)
            {
                readyQ.Add(u);
            }
            readyQ.Sort(delegate(Unit u1, Unit u2) { return u1.get_turn_charge().CompareTo(u2.get_turn_charge()); });
            readyQ.ForEach(delegate(Unit p) { Console.WriteLine(String.Format("{0} {1}", p.get_character(), p.get_turn_charge())); });
        }
        public void UpdateQ()
        {   current_unit.selected_tiles = new List<Vector2>();
            current_unit = readyQ[0]; //current unit is first in queue
            movement_paths = new Char[current_unit.Stats.get_movement() * 2 + 1, current_unit.Stats.get_movement() * 2 + 1]; //seeing as arrays are weird, first entry = y size, 2nd = x size
            //movement_paths = new Char[100, 100];
            current_unit.selected_tiles = new List<Vector2>();
            current_selectedTile = current_unit.get_position(); //move selected tile to the new unit
            camera_offset = current_unit.get_position() * 100;
            mouse_displacement = Vector2.Zero;
            mouse_displacement.Y -= field_object.get_tile_height(current_unit.get_position()) * 20;
            //this loop is used for testing
            for (int i = 0; i < current_unit.get_stats().get_movement() * 2 + 1; i++){
                for (int j = 0; j < current_unit.get_stats().get_movement() * 2 + 1; j++){
                    movement_paths[i, j] = 'M';
                }
            }
            FindMovementTiles(current_unit);
            readyQ.Remove(current_unit); //removes current unit from front of queue
            //adjusts the turn charge based on level and number of turns since last move
            foreach (Unit u in units)
            {
                int tmp;
                tmp = u.get_turn_charge() / u.get_stats().get_speed();
                tmp++;
                tmp *= u.get_stats().get_speed();
                u.set_turn_charge(tmp);
            }
            //sorts the ready queue
            readyQ.Sort(delegate(Unit u1, Unit u2) { return u2.get_turn_charge().CompareTo(u1.get_turn_charge()); });
            current_unit.set_turn_charge(0);//sets current unit turn charge to 0
            readyQ.Add(current_unit); //adds that unit to the end of the queue
        }
        public void SortUnits() //used in the proper sorting of units on the field because of alpha clipping
        {
            float a = 0, b = 0, offset = 0;
            units.Sort(delegate(Unit u1, Unit u2)
            {
                if (RotationY == 315){
                    a = u1.get_position().X + u1.get_position().Y;
                    b = u2.get_position().X + u2.get_position().Y;
                    offset = -0.5f;
                }else if (RotationY == 225){
                    a = u1.get_position().X + field_object.get_height() - u1.get_position().Y;
                    b = u2.get_position().X + field_object.get_height() - u2.get_position().Y;
                    offset =  0.5f;
                }else if (RotationY == 135){
                    a = field_object.get_height() + field_object.get_width() - u1.get_position().X - u1.get_position().Y;
                    b = field_object.get_height() + field_object.get_width() - u2.get_position().X - u2.get_position().Y;
                    offset =  0.5f;
                }else{
                    a = -u1.get_position().X + field_object.get_width() + u1.get_position().Y;
                    b = -u1.get_position().X + field_object.get_width() + u1.get_position().Y;
                    offset = -0.5f;
                }
                if (current_direction == 'R' || current_direction == 'D'){
                    if (u1 == current_unit) { a += offset; }
                    if (u2 == current_unit) { b += offset; }
                }else{
                    if (u1 == current_unit) { a -= offset; }
                    if (u2 == current_unit) { b -= offset; }
                }
                return a.CompareTo(b);
            });
        }
        public Boolean FindMovement(Char temp){
            //takes into account the rotation of the field and enables the controls to switch with rotation
            //used to properly move the selected tile where UP still moves the upper right tile, reguardless of rotation.
            current_direction = temp;
            if(RotationY==135){
                if(current_direction.Equals('R')){current_direction='L';}
                else if(current_direction.Equals('L')){current_direction='R';}
                else if(current_direction.Equals('U')){current_direction='D';}
                else if(current_direction.Equals('D')){current_direction = 'U';}
            }else if(RotationY==225){
                if(current_direction.Equals('L')){current_direction='D';}
                else if(current_direction.Equals('R')){current_direction='U';}
                else if(current_direction.Equals('D')){current_direction='R';}
                else if(current_direction.Equals('U')){current_direction='L';}
            }else if(RotationY==45){
                if(current_direction.Equals('L')){current_direction='U';}
                else if(current_direction.Equals('R')){current_direction='D';}
                else if(current_direction.Equals('D')){current_direction='L';}
                else if(current_direction.Equals('U')){current_direction='R';}
            }
            if (current_direction.Equals('R') && current_selectedTile.X < field_object.get_width()){
                return true;
            }else if (current_direction.Equals('L') && current_selectedTile.X > 0){
                return true;
            }else if(current_direction.Equals('U') && current_selectedTile.Y > 0){
                return true;
            }else if(current_direction.Equals('D') && current_selectedTile.Y < field_object.get_height()){
                return true;
            }else{return false;}

        }
        public void MoveSelectedTile(){
            if(current_direction == 'R'){
                current_selectedTile.X += 1;
            }else if(current_direction == 'L'){
                current_selectedTile.X -= 1;
            }else if(current_direction == 'U'){
                current_selectedTile.Y -= 1;
            }else if(current_direction == 'D'){
                current_selectedTile.Y += 1;
            }
        }
        public void FindMovementPath(Vector2 start, Vector2 finish){
            List<Vector2> temp = new List<Vector2>();
            float x = start.X - finish.X, y = start.Y - finish.Y;
            Vector2 differenceX = new Vector2(x==0?0:(x>0?1:-1),0);
            Vector2 differenceY = new Vector2(0, y==0?0:(y>0?1:-1));
            for(int i=0; i<Math.Abs(x); i++) {
                temp.Add(differenceX);
            }for(int i=0; i<Math.Abs(y); i++) {
                temp.Add(differenceY);
            }
            movements = temp;
        }
    }
} 